/**
 * \file    rule.h
 * \brief   Parser generator.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    September 17, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#ifndef RULE_H
#define RULE_H

#include <sstream>
#include "common.h"
#include "description.h"

LPP_NAMESPACE_BEGIN

class Rule {
public:
    typedef Rule __self;

    static RuleRef Make(Index const id
                         , Grammar & __g
                         , SymbolRef const & __s
                         , String const & __a
                         , Index const rule_line) {
        return std::make_shared<__self>(id,__g,__s,__a,rule_line);
    }

    Index index() const {/*! An index number for this rule. */
        return index_m;
    }
    void index(Index const i) {/*! Index number setter for this rule. */
        index_m = i;
    }
    Index id() const {           /* id for this rule */
        return id_m;
    }
    Grammar & grammar() const { /*! The owner of the rule. */
        return grammar_m;
    }
    Index rule_line() const { /*! Line number for the rule */
        return rule_line_m;
    }
    SymbolRef const & lhs_symbol() const {    /*! Left-hand side of the rule, owned by the grammar. */
        return lhs_symbol_m;
    }
    String const & lhs_alias() const {/*! Alias for the LHS (0 length if none) */
        return lhs_alias_m;
    }
    bool lhs_is_start() const { /*! True if left-hand side is the start symbol. */
        return id() == 0;
    }
    SymbolRefList const & rhs_symbols() const {/*! The RHS symbols */
        return rhs_symbols_m;
    }
    SymbolRef const & rhs_symbol_at(SymbolIndex const i) const {/*! The RHS symbol at given index. Throw out_of_range if relevant. */
        return rhs_symbols_m.at(i);
    }
    String const & rhs_alias_at(SymbolIndex const i) const {/*! The RHS alias at given index. Throw out_of_range if relevant. */
        return rhs_aliases_m.at(i);
    }
    bool is_at_end(SymbolIndex const dot) const {
        return dot>=rhs_symbols_m.size();
    }
    void add_rhs_symbol(SymbolRef const & S, String const & alias="") {
        rhs_symbols_m.push_back(S);
        hash_combine<SymbolRef>(hash_m,S);
        if (alias.empty()) {
            std::ostringstream oss;
            oss<<"$"<<rhs_symbols_m.size();
            rhs_aliases_m.push_back(oss.str());
        } else {
            rhs_aliases_m.push_back(alias);
        }
    }
    StringList const & rhs_aliases() const { /*! An alias for each RHS symbol (0 length if none) */
        return rhs_aliases_m;
    }
    Index non_nullable_rhs_count() const {
        return non_nullable_rhs_count_m;
    }
    void non_nullable_rhs_count(Index const n) {
        non_nullable_rhs_count_m = n;
    }
    StringList const & code_lines() const { /*! The translated code template to be executed when this rule is reduced */
        return code_lines_m;
    }
    StringList const & code_template_lines() const { /*! The code template to be executed when this rule is reduced */
        return code_template_lines_m;
    }
    void add_code_template_line(String const & s) {
        code_template_lines_m.push_back(s);
    }
    SymbolRef const & precedence_symbol() const {
        return precedence_symbol_m;
    }
    void precedence_symbol(SymbolRef const & symbol) {
        precedence_symbol_m = symbol;
    }
    String short_description() const;
    void translate_template_code(Parser & parser);

    friend class Grammar;
    Rule ( Index const id
         , Grammar & __g
         , SymbolRef const & __s
         , String const & __a
         , Index const rule_line)
        : id_m(id)
        , index_m(id)
        , grammar_m(__g)
        , rule_line_m(rule_line)
        , lhs_symbol_m(__s)
        , lhs_alias_m(__a) {
        hash_m = std::hash<SymbolRef>()(__s);
        // DEBUG_MESSAGE("+rule(%#lx): %lu/%lu",reinterpret_cast<std::uintptr_t>(this),id_m,rule_line_m);
    }
    ~Rule() {
        // DEBUG_MESSAGE("~rule(%#lx): %lu/%lu",reinterpret_cast<std::uintptr_t>(this),id_m,rule_line_m);
    }
public:
    size_t hash() const { /*! Two rules with the same lhs and rhs symbol ordered list have the same hash. */
        return hash_m;
    }
private:
    StringList rhs_aliases_m;
    SymbolRefList rhs_symbols_m;         /* The RHS symbols */
    Index non_nullable_rhs_count_m; /* An index number for this rule */
    StringList code_lines_m;              /* The translated code template to be executed when this rule is reduced */
    StringList code_template_lines_m;     /* The code template to be executed after translation when this rule is reduced */
    Index id_m;                          /* id for this rule */
    void id(Index const i) {
        index_m = id_m = i;
    }
    Index index_m;               /* An index number for this rule */
    Grammar & grammar_m;
    Index rule_line_m;
    SymbolRef lhs_symbol_m;
    String lhs_alias_m;   /* Alias for the LHS (0 length if none) */
    SymbolRef precedence_symbol_m;    /* Precedence symbol for this rule, used in conflict resolution */
    size_t hash_m;
};

LPP_NAMESPACE_END

#endif // RULE_H
